package com.kookapi.asserts.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kookapi.common.config.RuoYiConfig;
import com.kookapi.common.utils.file.FileUtils;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
import com.kookapi.asserts.AssertionComponent;
import com.kookapi.asserts.result.AssertionResult;
import io.restassured.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Set;

/**
 * @author bairimengchang
 * @version 1.0
 * @project java-test
 * @description JSON Schema校验断言组件（networknt版）
 * @date 2025/9/30
 */
@Component("jsonSchemaAssertionComponent")
@Slf4j
public class JsonSchemaAssertionComponent implements AssertionComponent {

    private final ObjectMapper objectMapper = new ObjectMapper();
    private final JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);

    @Override
    public AssertionResult execute(Response response, Map<String, Object> context) {
        String responseBody = response.getBody().asString();
        if (responseBody == null || responseBody.trim().isEmpty()) {
            return AssertionResult.failure("响应体为空");
        }

        // 选了这个断言组件就必须通过JSON Schema文件
        String jsonSchemaFile = (String) context.get("jsonSchemaFile");
        if (jsonSchemaFile == null) {
            return AssertionResult.failure("未指定JSON Schema文件");
        }

        log.info("使用Schema文件路径: {}", jsonSchemaFile);

        return validateJsonSchema(responseBody, jsonSchemaFile);
    }

    /**
     * 使用networknt进行JSON Schema校验
     */
    private AssertionResult validateJsonSchema(String responseBody, String jsonSchemaFile) {
        try {
            // 检查文件是否存在
            if (!exists(jsonSchemaFile)) {
                return AssertionResult.failure("Schema文件不存在: " + jsonSchemaFile);
            }

            // 读取Schema文件内容
            String schemaContent = readFileContent(jsonSchemaFile);

            JsonNode schemaNode = objectMapper.readTree(schemaContent);
            JsonNode responseNode = objectMapper.readTree(responseBody);

            JsonSchema schema = schemaFactory.getSchema(schemaNode);
            Set<ValidationMessage> validationMessages = schema.validate(responseNode);

            if (validationMessages.isEmpty()) {
                log.info("✅ JSON Schema校验通过");
                return AssertionResult.success("JSON Schema校验通过");
            } else {
                StringBuilder errorMsg = new StringBuilder("JSON Schema校验失败:\n");
                for (ValidationMessage message : validationMessages) {
                    errorMsg.append(" - ").append(message.getMessage()).append("\n");
                }
                return AssertionResult.failure(errorMsg.toString());
            }

        } catch (Exception e) {
            return AssertionResult.failure("Schema校验失败: " + e.getMessage());
        }
    }

    /**
     * 读取数据库存储的文件路径对应的文件内容
     * @param dbFilePath 数据库中的文件路径，如：/profile/upload/2025/10/10/xxx.json
     * @return 文件内容字符串
     */
    public static String readFileContent(String dbFilePath) throws IOException {
        // 1. 获取实际文件绝对路径
        String absolutePath = getAbsolutePath(dbFilePath);

        // 2. 读取文件内容
        return new String(Files.readAllBytes(Paths.get(absolutePath)));
    }

    /**
     * 将数据库存储的相对路径转换为绝对路径
     * @param dbFilePath 数据库中的文件路径
     * @return 绝对路径
     */
    public static String getAbsolutePath(String dbFilePath) {
        // 移除路径前缀（如果有）
        String relativePath = FileUtils.stripPrefix(dbFilePath);

        // 结合配置的基目录得到绝对路径
        return RuoYiConfig.getProfile() + File.separator + relativePath;
    }

    /**
     * 检查文件是否存在
     * @param dbFilePath 数据库中的文件路径
     * @return 是否存在
     */
    public static boolean exists(String dbFilePath) {
        String absolutePath = getAbsolutePath(dbFilePath);
        return new File(absolutePath).exists();
    }


}